#pragma once 
#include "Socket.hpp"
#include <sys/select.h>
#include "Log.hpp"
#include "Inetaddr.hpp"

namespace SelectModule
{
    using namespace SocketModule;
    using namespace LogModule;
    const int gdefualtfd = -1;
    #define NUM sizeof(fd_set)*8
    class SelectServer
    {
    private:
        uint16_t _port;
        std::shared_ptr<Socket> _listensocket;
        bool _isrunning;
        int _fd_array[NUM];
        void Dispacher(fd_set* rfds)
        {
            for (int i = 0; i <NUM; i++)
            {
                if (_fd_array[i] == gdefaultfd) continue;
                if (FD_ISSET(_fd_array[i],rfds))
                {
                    if (_fd_array[i] == _listensocket->FD())
                    {
                        //进行acceptor
                        AccePtor();
                    }
                    else 
                    {
                        //进行去取操作
                        ReadIo(i);
                    }
                }
            }
        }
        void AccePtor(){
            InetAddr client; 
            int newfd = _listensocket->Acceptor(&client);
            LOG(LogLevel::INFO) << "get a newfd, newfd is :  " << newfd << " client info: " << client.Info();
            int pos = -1;
            for (int i = 0; i < NUM; i++){
                if (_fd_array[i] == gdefaultfd) 
                {
                    pos = i;
                    break;
                }
            }
            if (pos == -1){
                LOG(LogLevel::INFO) << "辅助数组已满，无法关注新的连接";
                ::close(newfd); //一定要关闭!!!!!
                return;
            }
            _fd_array[pos] = newfd;
        }
        void ReadIo(int i)
        {
            char buffer[1024];
            int n = ::recv(_fd_array[i],buffer,sizeof(buffer)-1,0);
            if (n > 0){
                buffer[n] = 0;
                LOG(LogLevel::INFO) << "client say# " << buffer;
                std::string message = "echo server# ";
                message += buffer;
                ::send(_fd_array[i],message.c_str(),message.size(),0);
            }
            else if(n == 0)
            {
                LOG(LogLevel::INFO) << "client quit: " << _fd_array[i];
                ::close(_fd_array[i]);
                _fd_array[i] = gdefaultfd;
            }
            else 
            {
                LOG(LogLevel::INFO) << "read error, fd is : " << _fd_array[i];
                ::close(_fd_array[i]);
                _fd_array[i] = gdefaultfd;
            }
        }   
    public:
       SelectServer(uint16_t port):_port(port),_listensocket(std::make_shared<TcpSocket>()),_isrunning(false)
       {
            _listensocket->BuildTckSocket(_port);         
            for (int i = 0; i < NUM; i++) _fd_array[i] = gdefaultfd;   
       } 
       void Loop()
       {
            _isrunning = true;
            fd_set rfds;
            while(_isrunning)
            {
                int maxfd = gdefaultfd;
                struct timeval timeout = {10,0};
                FD_ZERO(&rfds);
                _fd_array[0] = _listensocket->FD(); 
                for (int i =0; i < NUM;i++)
                {
                    if (_fd_array[i] == gdefaultfd) continue;
                    if (_fd_array[i] > maxfd) maxfd = _fd_array[i];
                    FD_SET(_fd_array[i],&rfds);
                }
                //由于rfds是输入输出参数，所以我们并不会帮我们保存历史的fd，每一次都需要重新设置。
                int n = ::select(maxfd+1,&rfds,nullptr,nullptr,&timeout);
                switch (n)
                {
                case -1:
                    LOG(LogLevel::ERROR) << "读取失败,等待了错误的fd?";
                    break;
                case 0:
                    LOG(LogLevel::WARNING) << "time out";
                    break;
                default:
                    LOG(LogLevel::INFO) << "还剩余： " << timeout.tv_sec;
                    Dispacher(&rfds);
                    break;
                }
            }
            _isrunning = false;
       }
       ~SelectServer(){} 
    };
}